package com.derbysoft.lowcostadapter.dswitch3.service.impl;

import com.derbysoft.dswitch.dto.hotel.avail.AvailRoomStayDTO;
import com.derbysoft.dswitch.dto.hotel.avail.HotelAvailCriteriaDTO;
import com.derbysoft.dswitch.dto.hotel.common.RateDTO;
import com.derbysoft.dswitch.dto.hotel.common.StayDateRangeDTO;
import com.derbysoft.dswitch.remote.hotel.dto.HotelAvailRequest;
import com.derbysoft.dswitch.remote.hotel.dto.HotelAvailResponse;
import com.derbysoft.lowcostadapter.commons.exception.HotelNotFoundException;
import com.derbysoft.lowcostadapter.commons.model.Hotel;
import com.derbysoft.lowcostadapter.commons.model.RateCache;
import com.derbysoft.lowcostadapter.commons.model.Status;
import com.derbysoft.lowcostadapter.commons.redis.service.HotelCacheService;
import com.derbysoft.lowcostadapter.commons.redis.service.RateCacheService;
import com.derbysoft.lowcostadapter.commons.redis.service.impl.HSetEntity;
import com.derbysoft.lowcostadapter.commons.redis.utils.DateUtils;
import com.derbysoft.lowcostadapter.commons.redis.utils.KeyValueHelper;
import com.derbysoft.lowcostadapter.commons.repository.HotelRepository;
import com.derbysoft.lowcostadapter.commons.utils.DateTimeUtils;
import com.derbysoft.lowcostadapter.dswitch3.service.DS3FlushCacheService;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * User: jason
 * Date: 2012-11-01
 */
@Component("ds3FlushCacheService")
public class DS3FlushCacheServiceImpl implements DS3FlushCacheService {

    private static final int ONE_DAY = 24;
    private static final int ONE_THOUSAND = 1000;

    @Autowired
    private RateCacheService rateCacheService;

    @Autowired
    private HotelCacheService hotelCacheService;

    @Autowired
    private HotelRepository hotelRepository;

    @Override
    public void flushCache(HotelAvailRequest request, HotelAvailResponse response) {
        HotelAvailCriteriaDTO availCriteria = request.getHotelAvailRQ().getAvailCriteria();
        Hotel hotel = loadHotel(availCriteria.getHotelCodesList().get(0));
        int adult = availCriteria.getRoomStayCandidate().getAdultCount();
        int child = availCriteria.getRoomStayCandidate().getChildCount();
        StayDateRangeDTO stayDateRangeDTO = request.getHotelAvailRQ().getAvailCriteria().getStayDateRange();

        if (isNoAvailability(response)) {
            removeRates(hotel.getPropertyID(), adult, child, stayDateRangeDTO);
            return;
        }
        List<AvailRoomStayDTO> availRoomStayDTOs = response.getHotelAvailRS().getHotelAvailRoomStaysList().get(0).getRoomStaysList();

        saveRates(availRoomStayDTOs, String.valueOf(hotel.getPropertyID()), adult, child);
    }

    @Override
    public void removeRates(int propertyId, int adult, int child, StayDateRangeDTO stayDateRange) {
        int los = DateTimeUtils.getIntervalDays(stayDateRange.getCheckin(), stayDateRange.getCheckout());
        Date checkIn = DateTimeUtils.parse(stayDateRange.getCheckin());
        for (int i = 0; i < los; i++) {
            String rateKey = RateCache.PREFIX + KeyValueHelper.SEPARATOR + propertyId
                    + KeyValueHelper.SEPARATOR + DateUtils.format(DateUtils.addDays(checkIn, i))
                    + KeyValueHelper.SEPARATOR + adult
                    + KeyValueHelper.SEPARATOR + child;
            rateCacheService.removeAll(rateKey);
        }
    }

    private boolean isNoAvailability(HotelAvailResponse response) {
        return CollectionUtils.isNotEmpty(response.getHotelAvailRS().getNoAvailHotelsList())
                || CollectionUtils.isEmpty(response.getHotelAvailRS().getHotelAvailRoomStaysList())
                || CollectionUtils.isEmpty(response.getHotelAvailRS().getHotelAvailRoomStaysList().get(0).getRoomStaysList());
    }

    private void saveRates(List<AvailRoomStayDTO> availRoomStayDTOs, String propertyID, int adult, int child) {
        List<HSetEntity> hSetEntities = new ArrayList<HSetEntity>();
        for (AvailRoomStayDTO availRoomStayDTO : availRoomStayDTOs) {
            RateDTO rateDTO = availRoomStayDTO.getRoomRate().getRatesList().get(0);
            for (int i = 0; i < DateTimeUtils.getIntervalDays(rateDTO.getDateRange().getStart(), rateDTO.getDateRange().getEnd()) + 1; i++) {
                StringBuilder keyBuilder = new StringBuilder();
                keyBuilder.append(RateCache.PREFIX).append(KeyValueHelper.SEPARATOR);
                keyBuilder.append(propertyID).append(KeyValueHelper.SEPARATOR);
                keyBuilder.append(DateTimeUtils.formatDate(DateTimeUtils.addHours(DateTimeUtils.parse(rateDTO.getDateRange().getStart()), i * ONE_DAY))).append(KeyValueHelper.SEPARATOR);
                keyBuilder.append(adult).append(KeyValueHelper.SEPARATOR);
                keyBuilder.append(child);

                StringBuilder fieldBuilder = new StringBuilder();
                fieldBuilder.append(availRoomStayDTO.getRatePlan().getCode()).append(KeyValueHelper.SEPARATOR);
                fieldBuilder.append(availRoomStayDTO.getRoomType().getCode());

                long expireTime = DateTimeUtils.addHours(DateTimeUtils.parse(rateDTO.getDateRange().getStart()), (i + 2) * ONE_DAY).getTime() / ONE_THOUSAND;
                hSetEntities.add(HSetEntity.build(keyBuilder.toString(), fieldBuilder.toString(), rateDTO.getAmountBeforeTax().toString(), expireTime));
            }
        }

        rateCacheService.saveHSetEntities(hSetEntities);
    }

    private Hotel loadHotel(String propertyId) {
        Hotel hotel = hotelCacheService.loadById(propertyId);

        if (hotel == null) {
            hotel = hotelRepository.findByPropertyID(Integer.parseInt(propertyId), Status.ENABLED);
            if (hotel == null) {
                throw new HotelNotFoundException(propertyId);
            }
        }
        return hotel;
    }
}
